import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;

/** This class simplifies the process of reading the training files generated
 * by the ArtCollection class.  This became necessary when I decided that
 * I wanted to use compressed object streams to store the training data.
 * 
 * @author sgmr29
 *
 */
public class TrainingSetIterator implements Iterable<TrainingExample>, Iterator<TrainingExample>
{
	public static final String UNEXPECTED_ERROR =
		"An unexpected error occurred while reading training data";
	ObjectInputStream m_inStream;
	TrainingExample m_next;
	
	TrainingSetIterator()
	{
		readFile(ArtCollection.DEFAULT_TRAINING_FILE);
	}
	TrainingSetIterator (String trainingFile)
	{
		readFile(trainingFile);
	}
	
	@SuppressWarnings("unchecked")
	void readFile(String filename)
	{
		m_next = new TrainingExample();
		try {
			FileInputStream fileStream = new FileInputStream(filename);
			GZIPInputStream compressedStream = new GZIPInputStream(fileStream);
			m_inStream = new ObjectInputStream(compressedStream);
			m_next.answer = (ArrayList<Double>) m_inStream.readObject();
			m_next.question = (ArrayList<Double>) m_inStream.readObject();
		} catch (FileNotFoundException e) {
			System.out.println(UNEXPECTED_ERROR);
			e.printStackTrace();
			System.exit(1);
		} catch (EOFException e) {
			m_next.answer = null;
		} catch (ClassNotFoundException e) {
			System.out.println(UNEXPECTED_ERROR);
			e.printStackTrace();
			System.exit(1);
		} catch (IOException e) {
			System.out.println(UNEXPECTED_ERROR);
			e.printStackTrace();
			System.exit(1);
		}		
	}

	@Override
	public Iterator<TrainingExample> iterator() {
		return this;
	}

	@Override
	public boolean hasNext()
	{
		return m_next.isValid();
	}

	@SuppressWarnings("unchecked")
	@Override
	public TrainingExample next() {
		TrainingExample result = new TrainingExample(m_next);
		try {
			m_next.answer = (ArrayList<Double>) m_inStream.readObject();
			m_next.question = (ArrayList<Double>) m_inStream.readObject();
		} catch (EOFException e) {
			m_next.answer = null;
		} catch (ClassNotFoundException e) {
			System.out.println(UNEXPECTED_ERROR);
			e.printStackTrace();
			System.exit(1);
		} catch (IOException e) {
			System.out.println(UNEXPECTED_ERROR);
			e.printStackTrace();
			System.exit(1);
		}		
		return result;
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Used by Stephen to manually test this class as he writes it.
	 */
	public static void main(String[] args)
	{
		TrainingSetIterator iter = new TrainingSetIterator();
		ArtCollection art = new ArtCollection(ArtCollection.Method.RESOLUTION);
		
		int count = 0;
		for (TrainingExample example : iter)
		{
			if (count % 100 == 0)
			{
				System.out.print("Example #");
				System.out.print(count);
				System.out.println(":");
				System.out.print("Answer: ");
				//example.answer is an integer array, where each value is
				//either a zero or one.  This represents the expected OUTPUT
				//from a neural network
				System.out.println(example.answer);
				System.out.print("  Author : ");
				System.out.println(art.getAuthorPrediction(example.answer));
				System.out.print("  Title : ");
				System.out.println(art.getPiecePrediction(example.answer).title);
				//example.question is a double array, where each value is
				//between zero and one inclusive.  This represents the INPUT
				//to a neural network
				System.out.println("The first question:");
				System.out.println(example.question);
				
			}
			count += 1;
		}
		System.out.println("Number of questions:");
		System.out.println(count);
	}

}
